#!/usr/bin/env python
'''
	Nucleation
	http://code.google.com/p/nucleation/
	----
	Nucleation (an unofficial OpenFOAM Installer) lower the work to have foam in your system.

	Licensed under Simplified GPLv3 License (see LICENSE)
'''
#-----------------------------------------------------------------------
#    This file is part of Nucleation.
#
#    Nucleation is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Nucleation is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Nucleation.  If not, see <http://www.gnu.org/licenses/>.
#-----------------------------------------------------------------------
version="0.03beta"

from hashlib import md5
import subprocess
import optparse
import re
import platform
import sys

md5inst = md5()
def downloader(File, secondtry = False):
	"""Function to act like wget but that make md5 hash checks"""

	print 'Downloading ' + File['name']
	downloadedFile = urllib.urlretrieve('http://nucleation.googlecode.com/hg/' + File['name'])
	# Do md5 check to make sure the file downloaded properly
	f = file(downloadedFile[0], 'rb')
	part = f.read()
	md5inst.update(part)
	f.close()
	# Checke md5, if okey return file
	if md5inst.hexdigest() == File['md5']:
		return downloadedFile[0]
	else:
		# If not okey download try to download it again
		if secondtry == False:
			downloader(File, True)
		else:
			print agentFile['name'] + ' did not match its checksum. This can be caused by network issues.'
			sys.exit(2)

#Improvede user system detection
def usersystem():
    """Detect user system to provide info for Nucleation about running system"""
    usersystem = {'arch': platform.architecture(), 'platform': sys.platform, 'nCores': self.nCores()}
    if usersystem['platform'] == 'linux2':
        usersystem['Distro'] = platform.linux_distribution()
    elif usersystem['platform'] == 'darwin':
        usersystem['Version'] = platform.mac_ver()
    elif usersystem['platform'] == 'windows':
        usersystem['Version'] = platform.win32_ver()
    def nCores(self):
        if sys.platform == 'linux2':
            grep = subprocess.Popen(['grep', 'model name', '/proc/cpuinfo'], stdout=subprocess.PIPE, close_fds=True)
            wc = subprocess.Popen(['wc', '-l'], stdin=grep.stdout, stdout=subprocess.PIPE, close_fds=True)
            output = wc.communicate()[0]
            return int(output)
        if sys.platform == 'darwin':
            output = subprocess.Popen(['sysctl', 'hw.ncpu'], stdout=subprocess.PIPE, close_fds=True).communicate()[0].split(': ')[1]
            return int(output)
	return usersystem

def runBash(cmd):
	"""This function takes a bash command and run it"""
	p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
	out = p.stdout.read().strip()
	return out  #This is the stdout from the shell command

VERBOSE=False
def report(output,cmdtype="COMMAND:"):
	"""This funcition is as verbose function, it just prints the bash command"""
	if VERBOSE:
		print "%s: %s" % (cmdtype, output)
	else:
		print output

def updatescript():
	"""This function automatic search for new versions, compare md5, download and apply updates"""
	import httplib
	import json
	import sys
	from urllib2 import urlopen,HTTPError,URLError
	print 'Looking for a new version at our servers';
	#Try to acess the repository and get the JSON file with the md5 of lastest files
	try:
		request = urlopen('http://nucleation.googlecode.com/hg/LASTEST')
		response = request.read()
	except HTTPError, error:
		print 'FAIL trying to acess the file from our servers (HTTPError) = ' + str(error)
		sys.exit(2)
	except URLError, error:
		print 'FAIL trying to acess the file from our servers (URLError) = ' + str(error)
		sys.exit(2)
	except Exception, error:
		import traceback
		print 'FAIL trying to acess the file from our servers (Exception) = ' + traceback.format_exc()
		sys.exit(2)
	# Decode the JSON
	try:
		updateInfo = json.loads(response)
	except Exception, e:
		print 'We have had an error trying get the lastest version. Try again later.'
		sys.exit(2)
	# Do the version check
	if updateInfo['version'] != version:
		print 'There is a new version available.'
		# Download the new files
		for File in updateInfo['files']:
			File['tempFile'] = downloader(File)
		# If we got to here then everything worked out fine. However, all the files are still in temporary locations so we need to move them
		# This is to stop an update breaking a working agent if the update fails halfway through
		import os
		import shutil # Prevents [Errno 18] Invalid cross-device link (case 26878) - http://mail.python.org/pipermail/python-list/2005-February/308026.html
		for File in updateInfo['files']:
			print 'Updating ' + File['name']
			try:
				if os.path.exists(File['name']):
					os.remove(File['name'])
				shutil.move(File['tempFile'], File['name'])
			except OSError:
				print 'An OS error was raised. You will need to manually update from http://www.nucleation-installer.com'
				sys.exit(2)
		print 'Update completed. Now you can run it again.'
	else:
		print 'Nucleation is already up to date'
	pass

#Function to control option parsing in Python
def controller():
	"""Controller for shell behaviour"""
	global VERBOSE
	#Create instance of OptionParser Module, included in Standard Library
	p = optparse.OptionParser(description='Nucleation lower the work to have foam in your system', prog='Nucleation', version= version, usage= '%prog [option]')
	p.add_option('--verbose', '-v', action = 'store_true', help='prints verbosely', default=False)
	p.add_option('--update', action = 'store_true', help='Update Nucleation and required modules', default=False)
    #Option Handling passes correct parameter to runBash
	options, arguments = p.parse_args()
	if options.verbose:
		VERBOSE=True
	if options.update:
		updatescript()
	else:
		p.print_help()

#Runs all the functions
def main():
	controller()

#This idiom means the below code only runs when executed from command line
if __name__ == '__main__':
	main()
